// Package ratelimit
/**
* @Project : geektime-basic-go-study
* @File    : service.go
* @IDE     : GoLand
* @Author  : Tvux
* @Date    : 2024/10/21 16:30
**/

package ratelimit

import (
	"context"
	"fmt"
	"geektime-basic-go-study/webook/backend/internal/service/sms"
	"github.com/HJH0924/GenericGo/ratelimiter"
)

var ErrIsLimited = fmt.Errorf("触发限流")

// SMSRateLimitService 装饰器模式，为短信发送服务增加限流功能
type SMSRateLimitService struct {
	smsSvc  sms.Service
	limiter ratelimiter.RateLimiter
}

// SMSRateLimitServiceV2 下面这种叫组合，组合了 sms.Service，即实现了其所有方法，可以只装饰其中某几个方法
// 但是，sms.Service 字段是公开的，不是私有的，与上述引入一个私有字段没有什么优劣之分，具体看实现需要
// 使用组合，调用者可以绕开实现者的装饰器实现，可以只实现部分方法
// 不使用组合，调用者无法绕开，但必须实现所有方法
type SMSRateLimitServiceV2 struct {
	sms.Service
	limiter ratelimiter.RateLimiter
}

func (Self *SMSRateLimitService) GetProvider() sms.ProviderName {
	return Self.smsSvc.GetProvider()
}

func (Self *SMSRateLimitService) Send(ctx context.Context, tplId string, args []string, phones ...string) error {
	limited, err := Self.limiter.IsLimit(ctx, "sms-limiter")
	if err != nil {
		// 系统错误，有两种策略
		// 1、限流：保守策略，你的下游很坑的时候，
		// 2、不限：你的下游很强，业务可用性要求很高，即尽量容错策略
		// 这里返回错误，即采取保守策略
		return fmt.Errorf("短信服务判断是否限流异常：%w", err)
	}
	if limited {
		return ErrIsLimited
	}
	// 没有触发限流
	return Self.smsSvc.Send(ctx, tplId, args, phones...)
}

func NewSMSRateLimitService(smsSvc sms.Service, limiter ratelimiter.RateLimiter) *SMSRateLimitService {
	return &SMSRateLimitService{
		smsSvc:  smsSvc,
		limiter: limiter,
	}
}
